/***************************************************************************************************
 *Copyright(C),2010-2017,Sumscope
 *FileName	:  DictUpdateManager.h
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/04/07
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#pragma once

#include <core/allocator/allocator_pool_manager.h>
#include <core/container/SyncPtrQueue.h>
#include <core/system/event_wrapper.h>
#include <core/time/time.h>
#include <qbmessage/include/message_kernel.h>
#include <qbnet/include/network_service.h>
#include <qbprotocol/include/SSQBModel.h>

#include "DictManager.h"
#include "IBondDict.h"

using namespace qb;

#ifdef WIN32
#include <process.h>
#include <windows.h>
#else
#include <pthread.h>
#define HWND void*
#endif

namespace qb {
namespace ht {

// struct _xFileUpdateReq_t{
//	int  updateId;
//	char szCategory[32];
//	char szPatchName[32];
//	char szMd5[64];
//	int  sequence;	//唯一序列ID，暂不使用
// }Y_RD_PACKED;
// typedef struct _xFileUpdateReq_t xFileUpdateReq_t;
// struct xRespFileUpdateMsgMass_t	// 更新文件包的定义
//{
//	int  sequence;	//唯一序列ID，暂不使用
//	int  updateId;
//	char szCategory[32];
//	char szPatchName[32];
//	char szFileName[256];
//	int origsize;//原始文件长度
//	char szMd5[64];
//	int fileLen;
//	char *fileBuf;
// };

// typedef ss::base::SyncPtrQueue<void> UpdatePacketQueue;
// typedef SyncPtrQueue UpdatePacketQueue;
class DictLoaderListener;

/*
更新包对象分配器

*/
class PacketAllocator {
  qb::base::HeapAllocator m_heap;

 public:
  PacketAllocator();
  ~PacketAllocator() {}

  UpdatePacket* Create(const UpdatePacket* zipPacket);
  UpdatePacket* CreateFinishPacket(const UpdatePacket* zipPacket);
  void Release(UpdatePacket* rawPacket);
  void Reset();
};

struct DictCheckAck {
  long reqid;
  QBDictCheck_Ack ack;
};
}  // namespace ht
}  // namespace qb
/*
字典更新管理器
*/
using namespace qb::ht;
class ProgressNotifier {
  int m_start, m_end;     // 受控的进度范围
  int m_total_size;       // 总共要处理的数据,评估版本
  int m_real_total_size;  // 实际要处理的数据
  int m_processed_size;   // 已经处理的数据
  int m_progress;         // 当前进度,在[m_start,end]之间
  double m_stop_ratio;    // 模糊阶段与精确阶段的分界线
  bool m_recv_finished;   // 收取完毕
 public:
  ProgressNotifier();
  void Init(int start, int end);
  void SetDataSize(int bytes);
  void AddTotal(int bytes);
  void AddData(int bytes);
  void Finish();
};
namespace qb {
namespace ht {
class DickChecker;
class DictReqProgress;
class UpdatePackHistory;
}  // namespace ht
}  // namespace qb

class SyncPtrQueue;
class DictUpdateManager : public IDictUpdate,
                          public CUnknownObject<IMessageKernelObserver>,
                          public CUnknownObject<IMessageProcessorOnSubThread>,
                          public CUnknownObject<INetworkEventObserver> {
 public:
  enum kPullType {
    kPullTypeOnInit,            // 首次拉取字典
    kPullTypeOnSignal,          // 收到信号后拉取字典
    kPullTypeOnCheckFailure,    // 检查失败后拉取字典
    kPullTypeOnConnectRecover,  // 重新连接后拉取字典
    kPullTypeCount
  };
  enum kProgress {
    kProgressOk = 1,              // 成功
    kProgressTimeout = -60,       // 字典超时200s
    kProgressDisconnected = -61,  // 字典连接断开
    kProgressLoginKicked = -62    // 字典登录被踢
  };
  enum kState {  // 几种状态相互不叠加,同一时刻只能处于一种状态
    kStateIdle,  // 空闲中
    kStateReq,   // 正在发送请求消息
    kStateChk,   // 正在发送检查消息
    kStateExit,  // WaitForFinish调用后,m_eixting被设置成true
    kStateCount
  };
  struct DictCheckInfo {
    kDictType type;
    int level;  // kCheckLevel
    int nerror;
    int cerror;  // 连续几次错误
    QBDictCheck_Ack ack;
  };
  typedef std::map<kDictType, DictCheckInfo> FailedResult;

  virtual bool checkLoadStatus() override;

 protected:
  enum kConnect {
    kConnectNormal,
    kConnectLost,
    kConnectLostKicked,
    kConnectRecover
  };
  class DictReqSignal {
    /*
    收到的需要请求最新字典的信号的次数
    字典线程收到服务器推送的字典更新通知后,递增此变量
    字典线程在每次请求全部处理完毕后,将此变量归零,然后重新请求最新更新包
    同步:网络线程递增,字典线程归零
    用途:使得请求更新在每次处理结果完毕后才发生
    */
    int m_signal_count;            //
    volatile DWORD m_signal_tick;  // 上一次收到信号的时间
    qb::base::CSLock m_lock;

   public:
    DictReqSignal(int ncount)
        : m_signal_count(ncount), m_signal_tick(qb::base::GetHighResTime()) {}
    void Reset() {
      SCOPED_LOCK(m_lock);
      m_signal_count = 0;
    }
    void Add() {
      SCOPED_LOCK(m_lock);
      m_signal_count++;
      m_signal_tick = qb::base::GetHighResTime();
    }
    DWORD GetTick() const { return m_signal_tick; }
    int GetSignal() {
      SCOPED_LOCK(m_lock);
      return m_signal_count;
    }
  };
  typedef std::vector<qb::ht::DictLoaderListener*> LoadListeners;
  typedef qb::base::SyncPtrQueue<void> DictCheckQueue;
  // typedef SyncPtrQueue DictCheckQueue;
  DictManager m_dict_manager;    // 字典管理器
  SyncPtrQueue* m_raw_queue;     // 待处理的解压后的数据包
  DictCheckQueue m_check_queue;  // 字典检测队列
  int m_req_count;               // 请求次数,仅做调试用
  PacketAllocator m_allocator;
  ProgressNotifier m_progresser;
  DictReqSignal m_signal;
  UpdatePackHistory* m_history;

  /*
  请求结束后为true
  同步:网络线程检测请求结束后设置true,字典线程当其为true后设置false
  用途:使得更新数据到内存容器中在处理完毕后才发生
  */
  volatile bool m_is_requesting;  //

  FailedResult m_check_results;  // 字典检测结果
  /*
  当前正在处理的包类型
  每次发送一种请求后,改变为该请求
  */
  kDictType m_current_type;  // 当前字典类型
  int m_current_msgcount;    // 当前字典类型请求所收到的消息计数
  DWORD m_req_time;  // 当前字典请求时间,用于判断服务器端响应超时超时
  DWORD m_max_rsp_time;  // 字典最大响应时间,kProfileDictMaxRspTime
  DictReqProgress* m_req_progress;
  volatile bool m_eixting;             // 正在结束,调用了WaitForFinish
  volatile bool m_unloaded;            // 已经卸载了
  volatile bool m_first_wait;          // 当前处于首次等待模式
  volatile kConnect m_connect_status;  // 连接状态

#ifdef WIN32
  void* m_thread;
#else
  pthread_t m_thread;
#endif

  qb::ht::DickChecker* m_checker;
  qb::base::CSLock m_lock;
  volatile kState m_state;
  DWORD m_state_start;  // 当前状态的起始时间
  DWORD m_check_latter;
  DWORD m_last_conreq_time;  // 上次连接恢复后请求的时刻
  DWORD m_conreq_gap;        //
  DWORD m_signal_gap;
  DWORD m_max_check_time;  // 检查的最大时间，毫秒
  bool m_index_init;
  volatile bool m_needSyncFromServer = false;
  qb::EventWrapper* m_event = nullptr;

 public:
  static DictUpdateManager& instance();

  virtual void init() override;
  virtual void uninit() override;
  virtual void firstLoad() override;
  virtual int WaitFirstLoad(int start, int end) override;
  bool StartDictThread(bool force = false);
  void WaitForFinish();
  void OnPackReceived(int32_t wParam, void* lParam);
  void OnCheckReceived(void* wParam, void* lParam);
  void OnSignalReceived();
  void OnConnectSignal(int id);
  void DictThreadRun();

  void CheckDict();
  void ExportData(const char* fullDirectory);

  kState GetState() const { return m_state; }
  void SetState(kState state);

  bool NeedSyncFromSrv() const { return m_needSyncFromServer; }

 public:  // 调试用
  void DumpStatus(int mode = 0);

 protected:
  DictUpdateManager();
  ~DictUpdateManager();

  bool CheckConnectionLost();
  bool CheckConnectionRecover();
  int ProcessRawPackets(bool all, DWORD nowTime, DWORD batchTime = 200,
                        DWORD* pWorkTime = nullptr);
  void SendDictRequest(kDictType type);
  void SendFirstRequest(kPullType type);
  bool NotifyFinished(bool all);
  bool IsDataUpdated(kDictType type);

  void LoadDictCache(bool clear);

  void BuildSpellIndex();

  void SendNextRequest(kDictType type);

 protected:
  void OnCheck();
  void OnCheckFinished();
  void OnIdle();
  void OnSignal(DWORD tick);
  void OnRecover(DWORD tick);
  void ProcessFileUpdate_DATA(const char* RecBuf, xMsgHeadAck_t xHead);
  void FormatFileUpdate_DATA(const char* RecBuf, int msgtype,
                             xRespFileUpdateMsgMass_t& RecData);

  // IMessageKernelObserver
  STDMETHOD_(void, OnRecvMsg)(char* pData, int nDataLen) {}
  STDMETHOD_(void, OnRecvNetStatus)(qb::NetStatus status, short retcode);

  // IMessageProcessorOnSubThread
  STDMETHOD_(void, OnResponse)(THIS_ int32_t msgtype, qb::ProcessMessage msp);

  // INetworkEventObserver
  STDMETHOD_(void, OnForceQuit)(THIS_ int32_t connect_id);
  STDMETHOD_(void, OnDisConnect)(THIS_ int32_t connect_id);
  STDMETHOD_(void, OnRecover)(THIS_ int32_t connect_id);
};
